package ink.xiaobaibai.controller;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import ink.xiaobaibai.annotation.InsidePublicResources;
import ink.xiaobaibai.annotation.OutsidePublicResources;
import ink.xiaobaibai.common.ImageVerificationCode;
import ink.xiaobaibai.entity.AdminUser;
import ink.xiaobaibai.response.ResponseFormat;
import ink.xiaobaibai.service.IAdminUserService;
import ink.xiaobaibai.service.ICodeSendService;
import ink.xiaobaibai.service.impl.SmsCodeCreateServiceImpl;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.core.Authentication;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.bind.annotation.*;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @description: 非管理员可以使用的控制层
 * @author: 小白白
 * @create: 2021-05-17
 **/

@Slf4j
@RestController
@RequestMapping("/security")
@CrossOrigin
@Api(tags = "手机验证码控制层(忽略)")
public class HalfPublicController {

    private static final String PHONE_UUID = "phoneUUID:";

    private static final String BINDING_PHONE = "bindingPhone:";

    @Autowired
    private ICodeSendService imageCreateServiceImpl;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private ICodeSendService smsCodeCreateServiceImpl;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private IAdminUserService adminUserService;

    @GetMapping("/code/image")
    @OutsidePublicResources
    @ApiOperation("获取验证码")
    public void sendCodeImage(HttpServletRequest request, HttpServletResponse response) throws IOException {
        if (StringUtils.isBlank(request.getParameter("uuid")) || request.getParameter("uuid").length() < 8) {
            response.setCharacterEncoding("utf-8");
            response.setContentType("application/json; charset=utf-8");
            PrintWriter writer = response.getWriter();
            writer.write(JSON.toJSONString(ResponseFormat.fail("请携带合适的uuid")));
            return;
        }
        ImageVerificationCode imageCode = (ImageVerificationCode) this.imageCreateServiceImpl.createCode(request);
        System.out.println("当前的验证码:" + imageCode.getCode());
        ImageIO.write(imageCode.getImage(), "JPEG", response.getOutputStream());
    }

    /**
     * 绑定手机号码: 1.发送手机验证码  x换绑x
     *
     * @return success/error
     */
    @GetMapping("/bindingPhone/one")
    @InsidePublicResources
    @ApiOperation("绑定手机号: 1.发送验证码")
    public String bindingPhoneOne(Authentication authentication, HttpServletRequest request) {
        if (this.smsCodeCreateServiceImpl.createCode(request) != null) {
            //防止横向绑定
            this.stringRedisTemplate.opsForValue().set(BINDING_PHONE + authentication.getPrincipal().toString(), request.getParameter("phone"), 65, TimeUnit.SECONDS);
            return "success";
        }
        return "error";
    }

    /**
     * 绑定手机号码: 2.校验验证码
     *
     * @return success/error
     */
    @PostMapping("/bindingPhone/two")
    @InsidePublicResources
    @ApiOperation("绑定手机号: 2.校验验证码")
    public String bindingPhoneTwo(Authentication authentication, @RequestBody String code) {
        String userNumber = authentication.getPrincipal().toString();
        String phone = this.stringRedisTemplate.opsForValue().get(BINDING_PHONE + userNumber);
        if (phone == null) {
            return "error";
        }
        String cacheCode = this.stringRedisTemplate.opsForValue().get(SmsCodeCreateServiceImpl.SMS_CODE_PREFIX + phone);
        if (cacheCode == null || !StringUtils.equals(cacheCode, code)) {
            return "error";
        }
        //可以进行绑定了
        UpdateWrapper<AdminUser> u1 = new UpdateWrapper<>();
        u1.eq("user_number", userNumber);
        u1.set("user_phone", phone);
        this.adminUserService.update(u1);
        return "success";
    }

    /**
     * 通过手机验证码找回密码: 1.发送验证码
     */
    @GetMapping("/forgotPassword/one")
    @OutsidePublicResources
    @ApiOperation("通过手机找回密码: 1.发送验证码")
    public String forgotPasswordOne(HttpServletRequest request) {

        String uuid = request.getParameter("uuid");
        String phone = request.getParameter("phone");
        if (StringUtils.isBlank(phone) || StringUtils.isBlank(uuid)) {
            return "error";
        }
        //先查询库中是否有此手机号码
        QueryWrapper<AdminUser> q1 = new QueryWrapper<>();
        q1.eq("user_phone", phone);
        AdminUser adminUser = this.adminUserService.getOne(q1);
        if (adminUser == null) {
            return "error";
        }
        if (this.smsCodeCreateServiceImpl.createCode(request) == null) {
            return "error";
        }
        //uuid防止别人盗取短信 有效凭证->uuid
        this.stringRedisTemplate.opsForValue().set(PHONE_UUID + phone, uuid, 65, TimeUnit.SECONDS);
        return "success";
    }

    /**
     * 通过手机验证码找回密码: 2.校验验证码
     */
    @PostMapping("/forgotPassword/two")
    @OutsidePublicResources
    @ApiOperation("通过手机找回密码: 2.校验验证码")
    public String forgotPasswordTwo(@RequestBody Map<String, String> map) {

        String uuid = map.get("uuid");
        String phone = map.get("phone");
        String code = map.get("code");
        String password = map.get("password");
        if (StringUtils.isBlank(phone) || StringUtils.isBlank(uuid) || StringUtils.isBlank(code)) {
            return "error";
        }
        //校验phone和uuid
        String cacheUuid = this.stringRedisTemplate.opsForValue().get(PHONE_UUID + phone);
        if (cacheUuid == null || !StringUtils.equals(cacheUuid, uuid)) {
            return "error";
        }
        //校验code
        String cacheCode = this.stringRedisTemplate.opsForValue().get(SmsCodeCreateServiceImpl.SMS_CODE_PREFIX + phone);
        if (cacheCode == null || !StringUtils.equals(cacheCode, code)) {
            return "error";
        }
        //都已经校验完成,开始修改密码
        UpdateWrapper<AdminUser> u1 = new UpdateWrapper<>();
        u1.eq("user_phone", phone);
        u1.set("user_password", this.passwordEncoder.encode(password));
        boolean update = this.adminUserService.update(u1);
        if (!update) {
            log.info("修改密码出现未知错误:{}", phone);
            return "error";
        }
        return "success";
    }

}
